-- Values copied from Blizzard_MacroUI since it may not get loaded
local MAX_ACCOUNT_MACROS = 36;
local MAX_CHARACTER_MACROS = 18;

if not MacroMachine then
  MacroMachine = { };
end
MacroMachine.Enabled = true;

if not MacroMachineOptions then
  MacroMachineOptions = { };
end

if not MacroMachineCharacterOptions then
  MacroMachineCharacterOptions = { };
end

MacroMachine.CreateSpellTargetMacro = function()
  local targetName = UnitName("target");
  if targetName ~= nil then
    local macroName = nil;
    local macroText = nil;
    local macroIcon = 1;

    local focusFrame = GetMouseFocus();
    if focusFrame ~= nil then
      if focusFrame:IsFrameType("button") then
        if focusFrame["action"] ~= nil then
      	  local spellType, spellId, spellSubType = GetActionInfo(focusFrame.action);
      	  if spellType == "spell" then
      	    local spellName, rank = GetSpellName(spellId, "spell");
      	    macroName = spellName .. " " .. targetName;
      	    macroText = "/cast [target=" .. targetName .. "] " .. spellName .. "(" .. rank .. ")";
      	  elseif spellType == "macro" then
      	    local name, texture, macroBody, localVar = GetMacroInfo(spellId);
      	    if name ~= nil and macroBody ~= nil then
      	      macroName = name.. " " .. targetName;
      	      macroText = string.gsub(macroBody, "target=(%a+)", "target="..targetName);
      	    end
      	  end
        end
	  end
    end

    if macroName ~= nil and macroText ~= nil then
      MacroMachine.CreateMacro(macroName, macroIcon, macroText);
    end
  end
end

MacroMachine.CreateAssistMacro = function()
  local targetName = UnitName("target");
  if targetName ~= nil then
    local macroName = nil;
    if MacroMachineCharacterOptions["SA"] then
      macroName = MACROMACHINE_SINGLE_ASSIST_NAME;
    else
      macroName = MACROMACHINE_ASSIST_PREFIX..targetName;
    end

    local macroText = "/assist " ..targetName;
    MacroMachine.CreateMacro(macroName, MacroMachineOptions["AssistIcon"], macroText);
  end
end

MacroMachine.CreateMacro = function(macroName, macroIcon, macroText)
  local id = nil;
  if InCombatLockdown() == nil then
    local taggedName = macroName .. MACROMACHINE_TAG;
    id = GetMacroIndexByName(taggedName);
    if id > 0 then
      id = EditMacro(id, taggedName, macroIcon, macroText, 0, 1);
    else
      local numglobals, numchars = GetNumMacros();
      if numchars < MAX_CHARACTER_MACROS then
      	id = CreateMacro(taggedName, macroIcon, macroText, 1);
      elseif numglobals < MAX_ACCOUNT_MACROS then
        id = CreateMacro(taggedName, macroIcon, macroText, 0);
      else
        MacroMachine.Print(MACROMACHINE_NO_MACRO_SLOTS);
      end
    end
  else
    MacroMachine.Print(MACROMACHINE_CANNOT_CREATE_IN_COMBAT);
  end

  -- put the macro in the cursor
  if id ~= nil and id > 0 then
    ClearCursor();
    PickupMacro(id);
  end
end

MacroMachine.GetTaggedMacros = function(baseIndex, numMacros)
  local taggedIds = {};
  local id;
  local index = 0;
  local count = 0;
	for index = numMacros, 1, -1 do -- reverse order so they can be easily deleted
	  id = baseIndex + index;
    local name = GetMacroInfo(id);
    if name ~= nil then
	    local tagStart = strlen(name) - strlen(MACROMACHINE_TAG) + 1;
	    if tagStart >= 0 and (strsub(name, tagStart) == MACROMACHINE_TAG) then
	      count = count + 1;
	      taggedIds[count] = id;
	    end
	  end
  end
  return taggedIds, count;
end

MacroMachine.ClearTaggedMacros = function()
  local numglobal, numchars = GetNumMacros();
  local taggedIds, clearCount = MacroMachine.GetTaggedMacros(MacroMachine.BaseGlobalIndex, numglobal);
  for index=1, clearCount do
    DeleteMacro(taggedIds[index]);
  end
  
  taggedIds, clearCount = MacroMachine.GetTaggedMacros(MacroMachine.BaseCharacterIndex, numchars);
  for index=1, clearCount do
    DeleteMacro(taggedIds[index]);
  end
  return clearCount;
end

MacroMachine.ClearTaggedGlobalMacros = function()
	local numglobal = GetNumMacros();
  local taggedIds, clearCount = MacroMachine.GetTaggedMacros(MacroMachine.BaseGlobalIndex, numglobal);
  for index=1, clearCount do
    DeleteMacro(taggedIds[index]);
  end
  return clearCount;
end

MacroMachine.ClearTaggedCharacterMacros = function()
  local _, numchars = GetNumMacros();
  local taggedIds, clearCount = MacroMachine.GetTaggedMacros(MacroMachine.BaseCharacterIndex, numchars);
  for index=1, clearCount do
    DeleteMacro(taggedIds[index]);
  end
  return clearCount;
end

-----------------------------
-- SETUP CODE
-----------------------------

MacroMachine.OnLoad = function()
  this:RegisterEvent("VARIABLES_LOADED");
	
  --Set up the slash command
  SLASH_MacroMachine1 = "/mm";
  SlashCmdList["MacroMachine"] = MacroMachine.SlashHandler;
end

MacroMachine.OnEvent = function(self, event, ...)
  if event == "VARIABLES_LOADED" then
    MacroMachine.InitializeVariables();
    return;
  end
  
  if not MacroMachine.Enabled then
    return;
  end
end

-----------------------------
-- MOD CONFIGURATION CODE
-----------------------------

MacroMachine.Print = function(msg)
  if (DEFAULT_CHAT_FRAME) then
    DEFAULT_CHAT_FRAME:AddMessage(msg, 1.0, 1.0, 1.0);
  end
end

MacroMachine.SlashHandler = function(command)
  local i,j, cmd, param = string.find(command, "^([^ ]+) (.+)$");

  -- nil prevention
  if (cmd == nil) then cmd = command; end
  if (not cmd) then cmd = command; end
  if (not cmd) then cmd = ""; end
  if (not param) then param = ""; end
	
  local upperCmd = string.upper(cmd);
  if (upperCmd == "ON") then
    MacroMachine.Enabled = true;
    MacroMachine.Print(MACROMACHINE_ENABLED);
  elseif (upperCmd == "OFF") then
    MacroMachine.Enabled = false;
    MacroMachine.Print(MACROMACHINE_DISABLED);
  elseif (upperCmd == "S") then
    MacroMachine.CreateSpellTargetMacro();
  elseif (upperCmd == "A") then
  	MacroMachine.CreateAssistMacro();
  elseif (upperCmd == "C") then
    local count = MacroMachine.ClearTaggedMacros();
    if count > 0 then
    	MacroMachine.Print(MACROMACHINE_ALL_CLEAR);
    end
  elseif (upperCmd == "CG") then
    local count = MacroMachine.ClearTaggedGlobalMacros();
    if count > 0 then
    	MacroMachine.Print(MACROMACHINE_GLOBALS_CLEAR);
    end
  elseif (upperCmd == "CC") then
    local count = MacroMachine.ClearTaggedCharacterMacros();
    if count > 0 then
    	MacroMachine.Print(MACROMACHINE_CHARACTER_CLEAR);
    end
  elseif (upperCmd == "ASSISTICON") then
    local icon = tonumber(param);
    if icon ~= nil then
      if icon < 0 then
        icon = icon * -1;
      end
      MacroMachineOptions["AssistIcon"] = icon;
      MacroMachine.Print(MACROMACHINE_ICON_SET..icon);
    end
  elseif MACROMACHINE_OPTIONSDISPLAY[upperCmd] ~= nil then
    if MacroMachineOptions[upperCmd] == true then
      MacroMachineOptions[upperCmd] = nil;
      MacroMachine.Print(MACROMACHINE_OPTIONSDISPLAY[upperCmd] .. MACROMACHINE_OPTIONS_OFF);
    else
      MacroMachineOptions[upperCmd] = true;
      MacroMachine.Print(MACROMACHINE_OPTIONSDISPLAY[upperCmd] .. MACROMACHINE_OPTIONS_ON);
    end
  elseif ((cmd == "") or (cmd == "help")) then
    MacroMachine.Print(MACROMACHINE_USEAGE);
  end
end

MacroMachine.InitializeVariables = function()
  MacroMachine.BaseGlobalIndex = 0;
  MacroMachine.BaseCharacterIndex = MAX_ACCOUNT_MACROS;
  MacroMachine.CheckVariable("AssistIcon", 5);
  MacroMachine.CheckBooleanVariable("ONE", true);
end

MacroMachine.CheckVariable = function(variable, value)
  if MacroMachineOptions[variable] == nil then
    MacroMachineOptions[variable] = value;
  end
end

MacroMachine.CheckCharacterVariable = function(variable, value)
  if MacroMachineCharacterOptions[variable] == nil then
    MacroMachineCharacterOptions[variable] = value;
  end
end

MacroMachine.CheckBooleanVariable = function(variable, value)
  if MacroMachineOptions[variable] ~= true then
    MacroMachineOptions[variable] = value;
  end
end

MacroMachine.OffOrOn = function(val)
  if (val == nil) or (val == 0) then
    return MacroMachine_OPTIONS_OFF;
  else
    return MacroMachine_OPTIONS_ON;
  end
end
